// This file is part of OpenMeca, an easy software to do mechanical simulation.
//
// Author(s)    :  - Damien ANDRE  <openmeca@gmail.com>
//
// Copyright (C) 2012 Damien ANDRE
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#ifndef OpenMeca_Item_PartLink_hpp
#define OpenMeca_Item_PartLink_hpp

#include <boost/lexical_cast.hpp>

#include "OpenMeca/Item/Part.hpp"
#include "OpenMeca/Item/PartUserPoint.hpp"
#include "OpenMeca/Core/AutoRegisteredPtr.hpp"
#include "OpenMeca/Core/UserItemCommonProperty.hpp"
#include "OpenMeca/Core/None.hpp"



namespace OpenMeca
{  
  namespace Item
  {

    template <class T> class Link;
    
    // N correspond to body (1 or 2)
    template <class T, int N>
    class PartLink: public Part
    {
    
    public:
      static const std::string GetStrType(); 
      static void Init();

      typedef Core::None Dialog;

    public:
      PartLink(Link<T>& link);
      virtual ~PartLink();

      Link<T>& GetLink();
      const Link<T>& GetLink() const;

      void UpdateIcon();
      void DrawShape();


      Body& GetBody();
      const Body& GetBody() const;

      void Update();

      const Geom::Frame<_3D>& GetFrame() const;
      Geom::Frame<_3D>& GetFrame();

      const Geom::Frame<_3D>& GetReferenceFrame() const;


      
      const Util::Color& GetColor() const;
      Util::Color& GetColor();

      const Core::AutoRegisteredPtr<Link<T>, PartLink<T,N> >& GetLinkPtr() const;
      Core::AutoRegisteredPtr<Link<T>, PartLink<T,N> >& GetLinkPtr();

    private:
      PartLink(); //Not allowed, just for serialization
      PartLink(const PartLink<T,N>&);             //Not Allowed
      PartLink& operator=(const PartLink<T,N>&);  //Not Allowed

      QTreeWidgetItem& GetParentTreeItem();
      Core::SetOfBase<Core::UserItem> GetAssociatedSelectedItem();

      void UpdateName();

      friend class boost::serialization::access;
      template<class Archive> void save(Archive& ar, const unsigned int) const;
      template<class Archive> void load(Archive& ar, const unsigned int);
      BOOST_SERIALIZATION_SPLIT_MEMBER()


    private:
      Core::AutoRegisteredPtr<Link<T>, PartLink<T,N> > link_;
      Geom::Point<_3D> center_;
      Geom::Quaternion<_3D> quaternion_;
      Geom::Frame<_3D> frame_;
      Core::SetOf<PartUserPoint> points_;
    };


    template <class T, int N>  
    template<class Archive>
    inline void
    PartLink<T,N>::save(Archive& ar, const unsigned int) const
    {

      ar << BOOST_SERIALIZATION_BASE_OBJECT_NVP(Part);
      ar << BOOST_SERIALIZATION_NVP(points_);
    }


    template <class T, int N>  
    template<class Archive>
    inline void
    PartLink<T,N>::load(Archive& ar, const unsigned int) 
    {
      points_.ClearAndDelete();
      ar >> BOOST_SERIALIZATION_BASE_OBJECT_NVP(Part);
      ar >> BOOST_SERIALIZATION_NVP(points_);
    }

    
    template<class T, int N> 
    inline const std::string 
    PartLink<T,N>::GetStrType()
    {
      return std::string(Part::GetStrType() + '_' + T::GetStrType() + '_' 
			 + boost::lexical_cast<std::string>(N));
    }


    template<class T, int N> 
    inline void 
    PartLink<T,N>::Init()
    {
      Core::Singleton< Core::UserItemCommonProperty< PartLink<T,N> > >::Get().CreateAction_Nothing();
    }


    template<class T, int N>
    inline
    PartLink<T,N>::PartLink(Link<T>& link)
      :Part(GetStrType(), link.template GetBody<N>().GetMainTreeItem(), true),
       link_(*this, link),
       center_(boost::bind(&PartLink<T,N>::GetReferenceFrame, boost::ref(*this))),
       quaternion_(boost::bind(&PartLink<T,N>::GetReferenceFrame, boost::ref(*this))),
       frame_(center_, quaternion_),
       points_()
    {      
      UpdateName();
      T::template BuildPoints<N>(points_, *this);
    }

  
    template<class T, int N>
    inline
    PartLink<T,N>::~PartLink()
    {
    }


    template<class T, int N>
    inline Link<T>&
    PartLink<T,N>::GetLink()
    {
      return *link_.GetPtr();
    }

    template<class T, int N>
    inline const Link<T>& 
    PartLink<T,N>::GetLink() const
    {
      return *link_.GetPtr();
    }

    template<class T, int N>
    inline void
    PartLink<T,N>::Update()
    {
      UpdateName();
      Core::UserItem::Update();
    }


  


    template<class T, int N>
    inline const Geom::Frame<_3D>&
    PartLink<T,N>::GetReferenceFrame() const
    {
      return GetBody().GetFrame();
    }


    

    template<class T, int N>
    inline void
    PartLink<T,N>::UpdateName()
    {
      UserItem::GetName() = std::string(Part::GetStrType() + '_' + GetLink().GetName());
    }


    template<class T, int N>
    inline void
    PartLink<T,N>::UpdateIcon()
    {
      T::template UpdatePartIcon<N>(Core::UserItem::GetIcon(), GetBody().GetColor().GetQColor());
    }
    

    template<class T, int N>
    inline void
    PartLink<T,N>::DrawShape()
    {
      T::template DrawPart<N>();
    }


    template<class T, int N>
    inline Body& 
    PartLink<T,N>::GetBody()
    {
      return GetLink().template GetBody<N>();
    }
    
    template<class T, int N>
    inline const Body& 
    PartLink<T,N>::GetBody() const
    {
      return GetLink().template GetBody<N>();
    }

    template<class T, int N>
    inline QTreeWidgetItem& 
    PartLink<T,N>::GetParentTreeItem()
    {
      return GetLink().template GetBody<N>().GetMainTreeItem();
    }


    template<class T, int N>
    inline const Geom::Frame<_3D>& 
    PartLink<T,N>::GetFrame() const
    {
      return frame_;
    }

    template<class T, int N>
    inline Geom::Frame<_3D>& 
    PartLink<T,N>::GetFrame()
     {
      return frame_;
    } 

    template<class T, int N>
    inline const Util::Color& 
    PartLink<T,N>::GetColor() const
    {
      return GetLink().template GetBody<N>().GetColor();
    }
    
    template<class T, int N>
    inline Util::Color& 
    PartLink<T,N>::GetColor()
    {
      return GetLink().template GetBody<N>().GetColor();
    }


    template<class T, int N>
    inline const Core::AutoRegisteredPtr<Link<T>, PartLink<T,N> >& 
    PartLink<T,N>::GetLinkPtr() const
    {
      return link_;
    }


    template<class T, int N>
    inline Core::AutoRegisteredPtr<Link<T>, PartLink<T,N> >& 
    PartLink<T,N>::GetLinkPtr()
    {
      return link_;
    }


    template <class T, int N> 
    inline Core::SetOfBase<Core::UserItem> 
    PartLink<T,N>::GetAssociatedSelectedItem()
    {
      Core::SetOfBase<Core::UserItem> set;

      Core::SetOfBase<PartUserPoint>::it it;
      for (it = points_.Begin() ; it != points_.End(); it++ )
	set.AddItem(**it);

      return set;
    }



  }

}



namespace boost 
{ 
  namespace serialization 
  {
    template<class Archive, class T, int N>
    inline void save_construct_data(Archive & ar, 
				    const OpenMeca::Item::PartLink<T,N> * t, 
				    const unsigned int)
    {
      const OpenMeca::Item::Link<T>* linkPtr = t->GetLinkPtr().GetPtr();
      ar << linkPtr;
    }
    
    template<class Archive, class T, int N>
    inline void load_construct_data(Archive & ar, 
				    OpenMeca::Item::PartLink<T,N> * t, 
				    const unsigned int)
    {
      OpenMeca::Item::Link<T>* linkPtr = 0;
      ar >> linkPtr;
      ::new(t)OpenMeca::Item::PartLink<T,N>(*linkPtr);
    }
  }
} // namespace ...



#endif
